//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ListSmile.h"

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

#include <vector>
#include <algorithm>
#include <functional>

#include <fstream>
#include <iostream>
#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTH_LINGO 4
#define LENGHTINLINE 10000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListSmile::ListSmile(): fileGZ(false), lingoSimComplete(false) {
  this->filename            = new String();
  this->allSmilesMol        = new TListE <SmileStruct> ();
  this->lingoSimilarity     = NULL;
  this->averageSimDistance  = NULL;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListSmile::ListSmile(String* filename): lingoSimComplete(false) {
  this->filename            = new String(filename);
  this->allSmilesMol        = new TListE <SmileStruct> ();
  this->lingoSimilarity     = NULL;
  this->averageSimDistance  = NULL;
  this->fileGZ              = (filename->Contain("gz"))?true:false;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListSmile::~ListSmile() {

  if (this->filename) delete this->filename;

  if (this->lingoSimilarity) {
    for (int i = 0; i < this->allSmilesMol->Length(); i++)
      if (this->lingoSimilarity[i]) delete[] this->lingoSimilarity[i];
    if (this->lingoSimilarity) delete[] this->lingoSimilarity;
  }

  if (this->allSmilesMol)       delete this->allSmilesMol;
  if (this->averageSimDistance) delete[] this->averageSimDistance;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::Filename(String* filename) {

  if ((*(this->filename) == *filename) || !filename) return;
  
  this->filename->In(filename);
  this->fileGZ            = (this->filename->Contain("gz"))?true:false;
  this->lingoSimComplete  = false;

  this->ClearAverageSimDistance();
  this->ClearLingoSimilarity();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::AllSmiles(TListE <SmileStruct>* allSmilesMol) {
  if (allSmilesMol) this->allSmilesMol = allSmilesMol;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ListSmile::Filename(void) {
  return this->filename;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListSmile::FileGZ(void) {
  return this->fileGZ;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <SmileStruct>* ListSmile::AllSmiles(void) {
  return this->allSmilesMol;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double** ListSmile::LingoSimilarity(void) {
  return this->lingoSimilarity;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double* ListSmile::AverageSimDistance(void) {
  return this->averageSimDistance;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ExtractSmiles(void) {

  if (!this->filename->Length())  return 0;

  ifstream filestr;
  gzFile   filestrGZ;
  String   lineInput;
  char     buffer[LENGHTINLINE + 1];
  
  if (this->fileGZ) filestrGZ = gzopen(this->filename->Out(), "rb9");
  else              filestr.open (this->filename->Out(), ios::in);

  if (this->allSmilesMol->Length()) this->allSmilesMol->Clear();

  while (!((this->fileGZ)?gzeof(filestrGZ):filestr.eof())) {
    
    if (this->fileGZ) gzgets(filestrGZ, buffer, LENGHTINLINE);
    else              filestr.getline(buffer, LENGHTINLINE);

    lineInput.In(buffer); lineInput.Trim();
    
    if (lineInput.Length()) {
      SmileStruct *smile = new SmileStruct();
      smile->InputLine(lineInput);
      this->allSmilesMol->Add(smile);
    }
    
  }

  if (this->fileGZ) gzclose(filestrGZ);
  else              filestr.close();

  this->lingoSimComplete  = false;
  
  return this->allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ExtractSmiles(String* filename) {

  this->Filename(filename);
  return this->ExtractSmiles();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ExtractSmiles(int begin, int amount) {

  if (!this->filename->Length())  return 0;

  ifstream filestr;
  gzFile   filestrGZ;
  String   lineInput;
  int      numberLine = 0, counter = 0;
  char     buffer[LENGHTINLINE + 1];

  if (this->fileGZ) filestrGZ = gzopen(this->filename->Out(), "rb9");
  else              filestr.open (this->filename->Out(), ios::in);

  this->allSmilesMol->Clear();
  
  while (!((this->fileGZ)?gzeof(filestrGZ):filestr.eof())) {

    if (this->fileGZ) gzgets(filestrGZ, buffer, LENGHTINLINE);
    else              filestr.getline(buffer, LENGHTINLINE);

    lineInput.In(buffer); lineInput.Trim();
    
    if ((++numberLine >= begin) && (counter < amount) && (lineInput.Length())) {
      SmileStruct *smile = new SmileStruct();
      smile->InputLine(lineInput);
      this->allSmilesMol->Add(smile);
      counter++;
    }

    if (counter >= amount) break;
    
  }
  
  if (this->fileGZ) gzclose(filestrGZ);
  else              filestr.close();
  
  this->lingoSimComplete  = false;

  return this->allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ExtractSmiles(String* filename, int begin, int amount) {

  this->Filename(filename);
  return this->ExtractSmiles(begin, amount);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ExtractSmilesPos(long begin, long final) {

  if (!this->filename->Length())  return 0;

  ifstream filestr;
  gzFile   filestrGZ;
  String   lineInput;
  char     buffer[LENGHTINLINE + 1];
  long     endPosTmp = 0;

  if (this->fileGZ) { filestrGZ = gzopen(this->filename->Out(), "rb9"); gzseek(filestrGZ, begin, SEEK_SET); }
  else              { filestr.open (this->filename->Out(), ios::in);    filestr.seekg(begin, ios::beg);     }

  this->allSmilesMol->Clear();

  while (!((this->fileGZ)?gzeof(filestrGZ):filestr.eof())) {

    if (this->fileGZ) gzgets(filestrGZ, buffer, LENGHTINLINE); 
    else              filestr.getline(buffer, LENGHTINLINE); 

    lineInput.In(buffer); lineInput.Trim();
    
    if (this->fileGZ) endPosTmp = gztell(filestrGZ);
    else              endPosTmp = filestr.tellg();

    if (lineInput.Length()) {
      SmileStruct *smile = new SmileStruct();
      smile->InputLine(lineInput);
      this->allSmilesMol->Add(smile);
    }

    if (endPosTmp >= final) break;

  }

  if (this->fileGZ) gzclose(filestrGZ);
  else              filestr.close();

  this->lingoSimComplete  = false;

  return this->allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ExtractSmilesPos(String* filename, long begin, long final) {

  this->Filename(filename);
  return this->ExtractSmiles(begin, final);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ObtainLingos(void) {

  if (!this->allSmilesMol->Length()) this->ExtractSmiles();
  else {
    this->ClearLingoSimilarity();
    this->ClearAverageSimDistance();
  }
  
  this->allSmilesMol->SetInitial();
  
  for (int i = 0; i < this->allSmilesMol->Length(); i++)
    this->allSmilesMol->GetNext()->ObtainLingos();
  
  this->lingoSimComplete  = false;
  
  return this->allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ObtainLingos(String* filename) {

  this->Filename(filename);
  this->ExtractSmiles();
  
  return this->ObtainLingos();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListSmile::ObtainSimilarityMatrix(void) {

  this->ClearLingoSimilarity();

  if (!this->allSmilesMol->Length()) {
    this->ObtainLingos();
    if (!this->allSmilesMol->Length()) return false;
  }

  this->lingoSimilarity = new double*[this->allSmilesMol->Length()];

  this->allSmilesMol->SetInitial();
  TNode <SmileStruct>* refI = this->allSmilesMol->GetNextNode();
  
  for (int i = 0; i < this->allSmilesMol->Length(); i++, refI = refI->Next()) {

    this->lingoSimilarity[i] = new double[this->allSmilesMol->Length()];
    this->allSmilesMol->SetInitial();
    TNode <SmileStruct>* refJ = this->allSmilesMol->GetNextNode();

    for (int j = 0; j <= i; j++, refJ = refJ->Next())
      if (i != j) this->lingoSimilarity[i][j] = this->lingoSimilarity[j][i] = refI->Info()->ComputeLingoSimilarity(refJ->Info());
      else 				this->lingoSimilarity[i][j] = 1;

  }

  this->lingoSimComplete  = true;
  
  return this->lingoSimComplete;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListSmile::ObtainSimilarityMatrixWithoutNumber(void) {

  this->ClearLingoSimilarity();

  if (!this->allSmilesMol->Length()) {
    this->ObtainLingos();
    if (!this->allSmilesMol->Length()) return false;
  }

  this->lingoSimilarity = new double*[this->allSmilesMol->Length()];

  this->allSmilesMol->SetInitial();
  TNode <SmileStruct>* refI = this->allSmilesMol->GetNextNode();

  for (int i = 0; i < this->allSmilesMol->Length(); i++, refI = refI->Next()) {

    this->lingoSimilarity[i] = new double[this->allSmilesMol->Length()];
    this->allSmilesMol->SetInitial();
    TNode <SmileStruct>* refJ = this->allSmilesMol->GetNextNode();

    for (int j = 0; j <= i; j++, refJ = refJ->Next())
      if (i != j) this->lingoSimilarity[i][j] = this->lingoSimilarity[j][i] = refI->Info()->ComputeLingoSimilarityWithoutNumber(refJ->Info());
      else 				this->lingoSimilarity[i][j] = 1;

  }

  this->lingoSimComplete  = true;

  return this->lingoSimComplete;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListSmile::ObtainDistanceBySimilarityMatrix(void) {

  this->ClearLingoSimilarity();

  if (!this->allSmilesMol->Length()) {
    this->ObtainLingos();
    if (!this->allSmilesMol->Length()) return false;
  }

  this->lingoSimilarity = new double*[this->allSmilesMol->Length()];
  for (int i = 0; i < this->allSmilesMol->Length(); i++) 
    this->lingoSimilarity[i] = new double[this->allSmilesMol->Length()];
  
  this->allSmilesMol->SetInitial();
  TNode <SmileStruct>* refI = this->allSmilesMol->GetNextNode();

  for (int i = 0; i < this->allSmilesMol->Length(); i++, refI = refI->Next()) {

    this->allSmilesMol->SetInitial();
    TNode <SmileStruct>* refJ = this->allSmilesMol->GetNextNode();

    for (int j = 0; j <= i; j++, refJ = refJ->Next()) 
      if (i != j) this->lingoSimilarity[i][j] = this->lingoSimilarity[j][i] = refI->Info()->ComputeLingoSimilarity(refJ->Info()) - 1;
      else 				this->lingoSimilarity[i][j] = 0;

  }

  this->lingoSimComplete  = true;

  return this->lingoSimComplete;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListSmile::ObtainDistanceBySimilarityMatrixWithoutNumber(void) {

  this->ClearLingoSimilarity();

  if (!this->allSmilesMol->Length()) {
    this->ObtainLingos();
    if (!this->allSmilesMol->Length()) return false;
  }

  this->lingoSimilarity = new double*[this->allSmilesMol->Length()];
  for (int i = 0; i < this->allSmilesMol->Length(); i++)
    this->lingoSimilarity[i] = new double[this->allSmilesMol->Length()];

  TNode <SmileStruct>* refI = this->allSmilesMol->GetFirstNode();

  for (int i = 0; i < this->allSmilesMol->Length(); i++, refI = refI->Next()) {

    TNode <SmileStruct>* refJ = this->allSmilesMol->GetFirstNode();

    for (int j = 0; j <= i; j++, refJ = refJ->Next())
      if (i != j) this->lingoSimilarity[i][j] = this->lingoSimilarity[j][i] = refI->Info()->ComputeLingoSimilarityWithoutNumber(refJ->Info()) - 1;
      else 				this->lingoSimilarity[i][j] = 0;

  }

  this->lingoSimComplete  = true;

  return this->lingoSimComplete;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ObtainAverageSimMatrix(void) {

  if (this->averageSimDistance) delete[] this->averageSimDistance;
  if (!this->lingoSimComplete)  this->ObtainSimilarityMatrix();

  if (!this->allSmilesMol->Length()) return 0;

  this->averageSimDistance = new double[this->allSmilesMol->Length()];
  for (int i = 0; i < this->allSmilesMol->Length(); i++) {
    this->averageSimDistance[i] = 0;
    for (int j = 0; j < this->allSmilesMol->Length(); j++)
      if (i != j) this->averageSimDistance[i] += this->lingoSimilarity[i][j];
    this->averageSimDistance[i] /= (this->allSmilesMol->Length() - 1);
  }
  
  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ObtainAverageSimMatrixWithoutNumber(void) {

  if (this->averageSimDistance) delete[] this->averageSimDistance;
  if (!this->lingoSimComplete)  this->ObtainSimilarityMatrixWithoutNumber();

  if (!this->allSmilesMol->Length()) return 0;

  this->averageSimDistance = new double[this->allSmilesMol->Length()];
  for (int i = 0; i < this->allSmilesMol->Length(); i++) {
    this->averageSimDistance[i] = 0;
    for (int j = 0; j < this->allSmilesMol->Length(); j++)
      if (i != j) this->averageSimDistance[i] += this->lingoSimilarity[i][j];
    this->averageSimDistance[i] /= (this->allSmilesMol->Length() - 1);
  }

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ObtainAverageDistanceBySimMatrix(void) {

  if (this->averageSimDistance) delete[] this->averageSimDistance;
  if (!this->lingoSimComplete)  this->ObtainDistanceBySimilarityMatrix();

  if (!this->allSmilesMol->Length()) return 0;

  this->averageSimDistance = new double[this->allSmilesMol->Length()];
  for (int i = 0; i < this->allSmilesMol->Length(); i++) {
    this->averageSimDistance[i] = 0;
    for (int j = 0; j < this->allSmilesMol->Length(); j++)
      if (i != j) this->averageSimDistance[i] += this->lingoSimilarity[i][j];
    this->averageSimDistance[i] /= (this->allSmilesMol->Length() - 1);
  }

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int ListSmile::ObtainAverageDistanceBySimMatrixWithoutNumber(void) {

  if (this->averageSimDistance) delete[] this->averageSimDistance;
  if (!this->lingoSimComplete)  this->ObtainDistanceBySimilarityMatrixWithoutNumber();

  if (!this->allSmilesMol->Length()) return 0;

  this->averageSimDistance = new double[this->allSmilesMol->Length()];
  for (int i = 0; i < this->allSmilesMol->Length(); i++) {
    this->averageSimDistance[i] = 0;
    for (int j = 0; j < this->allSmilesMol->Length(); j++)
      if (i != j) this->averageSimDistance[i] += this->lingoSimilarity[i][j];
    this->averageSimDistance[i] /= (this->allSmilesMol->Length() - 1);
  }

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::RandomFile(int iteration = 1) {

  if (!this->filename->Length() || !this->ExtractSmiles()) return;
  
  this->ClearAverageSimDistance();
  this->ClearLingoSimilarity();

  //Random process
  vector <int> randomFlag;
  
  for (int i = 0; i < this->allSmilesMol->Length(); ++i) randomFlag.push_back(i);
  for (int i = 0; i < iteration; i++)                    random_shuffle(randomFlag.begin(), randomFlag.end());
  
  fstream filestr;
  gzFile  filestrGZ;
  
  if (this->fileGZ) filestrGZ = gzopen(this->filename->Out(), "wb9");
  else              filestr.open(this->filename->Out(), ios::out);

  for (int i = 0; i < this->allSmilesMol->Length(); i++) 
    if (this->fileGZ) gzprintf(filestrGZ, "%s %s\n", this->allSmilesMol->Get(randomFlag.at(i))->Code(), this->allSmilesMol->Get(randomFlag.at(i))->Id());
    else              filestr << this->allSmilesMol->Get(randomFlag.at(i))->Code() << " " << this->allSmilesMol->Get(randomFlag.at(i))->Id() << endl;

  if (this->fileGZ) gzclose(filestrGZ);
  else              filestr.close();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::ReadFile() {

  if (!this->filename->Length() || !this->ExtractSmiles()) return;

  this->ClearAverageSimDistance();
  this->ClearLingoSimilarity();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
long ListSmile::OrderFile(String* initialFileName, String* nameFileClusterMol, int ancestor, String* orderWay) {

  long numberMolecules = 0;
  
  if (!initialFileName || !initialFileName->Length() || !nameFileClusterMol || !nameFileClusterMol->Length()) return numberMolecules;

  ListAncestorMol* listAncestorMol = new ListAncestorMol(nameFileClusterMol, initialFileName);
  
  listAncestorMol->LoadDataFiles();

  /*
  if      (*orderWay == "CENTERED")   listAncestorMol->OrderListByAncestorCenteredIterative();
  else if (*orderWay == "ASCENDENT")  listAncestorMol->OrderListByAncestorAscendentIterative();
  else if (*orderWay == "DESCENDENT") listAncestorMol->OrderListByAncestorDescendentIterative();
  */
  
  listAncestorMol->WriteSmilesFile();

  numberMolecules = listAncestorMol->ListAncestor()->Length();

  if (listAncestorMol) delete listAncestorMol;

  return numberMolecules;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
long ListSmile::ReduceFile(String* initialFileName, String* nameFileClusterMol) {

  long numberMolecules = 0;

  if (!initialFileName || !initialFileName->Length() || !nameFileClusterMol || !nameFileClusterMol->Length()) return numberMolecules;

  ListSmile*        listInitialFile = new ListSmile(initialFileName);
  ListAncestorMol*  listClusterFile = new ListAncestorMol();

  listClusterFile->FilenameAncestor(nameFileClusterMol);

  listInitialFile->ReadFile();
  listClusterFile->LoadDataAncestorFiles();

  numberMolecules = listInitialFile->allSmilesMol->Length();

  fstream filestr;
  gzFile  filestrGZ;

  bool condFileGZ = initialFileName->Contain("gz");

  if (!condFileGZ) filestr.open(initialFileName->Out(), fstream::out);
  else             filestrGZ = gzopen(initialFileName->Out(), "wb9");

  listClusterFile->ListAncestor()->SetInitial();
  while (listClusterFile->ListAncestor()->SetNext()) {
    listInitialFile->allSmilesMol->SetInitial();
    while (listInitialFile->allSmilesMol->SetNext()) 
      if (!strcmp(listInitialFile->allSmilesMol->GetCurrent()->Id(), listClusterFile->ListAncestor()->GetCurrent()->ListIdAncestor()->GetFirst()->Out())) {
        if (!condFileGZ) filestr << listInitialFile->allSmilesMol->GetCurrent()->OutToSmileString() << endl;
        else             gzwrite(filestrGZ, listInitialFile->allSmilesMol->GetCurrent()->OutToSmileString(), strlen(listInitialFile->allSmilesMol->GetCurrent()->OutToSmileString()));
        break;
      }
  }

  if (!condFileGZ) filestr.close();
  else             gzclose(filestrGZ);

  if (listInitialFile) delete listInitialFile;
  if (listClusterFile) delete listClusterFile;

  return numberMolecules;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::OrderFileByAncestorCluster(String* initialFileName, String* nameFileClusterMol) {

  if (!initialFileName && !nameFileClusterMol) return;

  this->allSmilesMol->Clear();

  ListAncestorMol *listAncestorMol = new ListAncestorMol();
  
  listAncestorMol->FilenameAncestor(nameFileClusterMol);
  listAncestorMol->FilenameSmiles(initialFileName);

  listAncestorMol->LoadDataFiles();

  listAncestorMol->OrderListByAncestorAscendentIterative();
  listAncestorMol->FilenameSmiles(this->filename);
  listAncestorMol->WriteSmilesFile();
  listAncestorMol->WriteAncestorFile();

  if (listAncestorMol) delete listAncestorMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::ReduceFileToAncestorLevel(String* initialFileName, String* nameFileClusterMol, int level) {
    
  if (!initialFileName && !nameFileClusterMol) return;

  this->allSmilesMol->Clear();

  ListAncestorMol *listAncestorMol = new ListAncestorMol();

  listAncestorMol->FilenameAncestor(nameFileClusterMol);
  listAncestorMol->FilenameSmiles(initialFileName);

  listAncestorMol->LoadDataFiles();

  if (level <= listAncestorMol->ListAncestor()->Length()) {
    listAncestorMol->OrderListByAncestorAscendentIterative();
    listAncestorMol->WriteSmilesAncestorFile(level, this->filename);
  }

  if (listAncestorMol) delete listAncestorMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::GetClusterByAncestorLevel(String* initialFileName, String* nameFileClusterMol, int level) {

  if (!initialFileName && !nameFileClusterMol) return;

  this->allSmilesMol->Clear();

  ListAncestorMol *listAncestorMol = new ListAncestorMol();

  listAncestorMol->FilenameAncestor(nameFileClusterMol);
  listAncestorMol->FilenameSmiles(initialFileName);

  listAncestorMol->LoadDataFiles();

  if (level <= listAncestorMol->ListAncestor()->Length()) {
    listAncestorMol->OrderListByAncestorAscendentIterative();
    listAncestorMol->WriteSmilesAncestorFile(level, this->filename);
  }

  if (listAncestorMol) delete listAncestorMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double ListSmile::ObtainLingoSim(char* lingoString1, char* lingoString2) {

  int length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  char tmpLingo1[length1 + 1], tmpLingo2[length2 + 1];
  int  i, j, k, z;

  //Change the Cl by L and Br by R in two lingo sequences
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    if (i < length1) {
      if      ((lingoString1[i] == 'l') || (lingoString1[i] == 'r')) tmpLingo1[j - 1] = (lingoString1[i] == 'l')?'L':'R';
      else if (isdigit(lingoString1[i])) tmpLingo1[j++] = '?';
      else tmpLingo1[j++] = lingoString1[i];
    }
    if (i < length2) {
      if ((lingoString2[i] == 'l') || (lingoString2[i] == 'r')) tmpLingo2[k - 1] = (lingoString2[i] == 'l')?'L':'R';
      else if (isdigit(lingoString2[i])) tmpLingo2[k++] = '?';
      else tmpLingo2[k++] = lingoString2[i];
    }
  }
  tmpLingo1[j] = '\0'; tmpLingo2[k] = '\0';

  j -= (LENGTH_LINGO - 1); k -= (LENGTH_LINGO - 1);
  int    L = j + k; //Total Lingos in two sequences
  double similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {

      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {

      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1 - ((Nai - Nbi)/(Nai + Nbi))):(1 - ((Nbi - Nai)/(Nai + Nbi))));
    else                 L--;
  }

  return similarityValue/L;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::ClearLingoSimilarity() {
  if (this->lingoSimilarity) {
    for (int i = 0; i < this->allSmilesMol->Length(); i++)
      if (this->lingoSimilarity[i]) delete[] this->lingoSimilarity[i];
    if (this->lingoSimilarity) delete[] this->lingoSimilarity;
    this->lingoSimilarity = NULL;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::ClearAverageSimDistance() {
  if (this->averageSimDistance) delete[] this->averageSimDistance;
  this->averageSimDistance = NULL;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListSmile::WriteLingoSimMatrix(String* outputFile) {


  if (!this->allSmilesMol || !this->allSmilesMol->Length()) return;

  fstream fileLingoSimMatrix;

  fileLingoSimMatrix.open(outputFile->Out(), fstream::out);

  if (fileLingoSimMatrix.is_open()) {

    for (long i = 0; i < this->allSmilesMol->Length(); i++) {
      for (long j = 0; j < this->allSmilesMol->Length(); j++) {
        fileLingoSimMatrix.width(10);
        fileLingoSimMatrix << this->lingoSimilarity[i][j];
      }
      fileLingoSimMatrix << endl;
    }

    fileLingoSimMatrix.close();

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListSmile ListSmile::operator=(const ListSmile& listSmile) {

  *(this->filename)     = *(listSmile.filename);
  *(this->allSmilesMol) = *(listSmile.allSmilesMol);

  this->ClearAverageSimDistance();
  this->ClearLingoSimilarity();

  this->lingoSimilarity = new double*[this->allSmilesMol->Length()];

  for (int i = 0; i < this->allSmilesMol->Length(); i++) {
    this->lingoSimilarity[i] = new double[this->allSmilesMol->Length()];
    for (int j = 0; j <= i; j++)
      this->lingoSimilarity[i][j] = this->lingoSimilarity[j][i] = listSmile.lingoSimilarity[i][j];
  }

  this->averageSimDistance = new double[this->allSmilesMol->Length()];
  for (int i = 0; i < this->allSmilesMol->Length(); i++)
    this->averageSimDistance[i] = listSmile.averageSimDistance[i];

  this->fileGZ            = listSmile.fileGZ;
  this->lingoSimComplete  = listSmile.lingoSimComplete;

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
